#include "DX11HelpfulFunctions.h"
#include "ScreenManager.h"
#include "CameraManager.h"

void DX11::ClearColour(float r, float g, float b, float a)
{
    float bgColour[4] = {r, g, b, a}; // red,green,blue,alpha
	// clear bg colour
	TheScreenManager::Instance()->GetDeviceContext()->ClearRenderTargetView(
		*TheScreenManager::Instance()->GetRenderTargetview(), bgColour);
	// clear stencil buffer
	TheScreenManager::Instance()->GetDeviceContext()->ClearDepthStencilView(
		TheScreenManager::Instance()->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1.0f, 0);
}

void DX11::CreateRenderTexture(	ID3D11RenderTargetView** pTargetView,
								ID3D11ShaderResourceView** pShaderResource,
								const float clearColour[])
{
	// sets render target
	TheScreenManager::Instance()->GetDeviceContext()->OMSetRenderTargets(1, pTargetView, 
		TheScreenManager::Instance()->GetDepthStencilView());
	// clears render target
	TheScreenManager::Instance()->GetDeviceContext()->ClearRenderTargetView(*pTargetView, clearColour);
	// clears depth buffer	
	TheScreenManager::Instance()->GetDeviceContext()->ClearDepthStencilView(
		TheScreenManager::Instance()->GetDepthStencilView(), D3D11_CLEAR_DEPTH, 1, 0);
}

bool DX11::InitRenderToTexture(ID3D11Texture2D** pRenderTargetTexture, ID3D11RenderTargetView** pRenderTargetView, 
	ID3D11ShaderResourceView** pShaderResourceView)
{
	D3D11_TEXTURE2D_DESC textureDesc;
	D3D11_RENDER_TARGET_VIEW_DESC renderTargetViewDesc;
	D3D11_SHADER_RESOURCE_VIEW_DESC shaderResourceViewDesc;

	// Initialize the texture description.
	ZeroMemory(&textureDesc, sizeof(textureDesc));
	
	// Setup the texture description.
	RECT rc;
	GetClientRect(TheScreenManager::Instance()->GetWindowHandler() , &rc);
	UINT width = rc.right - rc.left;
	UINT height = rc.bottom - rc.top;
	textureDesc.Width = width;
	textureDesc.Height = height;
	textureDesc.MipLevels = 1;
	textureDesc.ArraySize = 1;
	textureDesc.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
	textureDesc.SampleDesc.Count = 1;
	textureDesc.Usage = D3D11_USAGE_DEFAULT;
	textureDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
	textureDesc.CPUAccessFlags = 0;
	textureDesc.MiscFlags = 0;

	// Create the texture
	TheScreenManager::Instance()->GetDevice()->CreateTexture2D(&textureDesc, NULL, &*pRenderTargetTexture);

	// Setup the description of the render target view.
	renderTargetViewDesc.Format = textureDesc.Format;
	renderTargetViewDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
	renderTargetViewDesc.Texture2D.MipSlice = 0;

	// Create the render target view.
	TheScreenManager::Instance()->GetDevice()->CreateRenderTargetView(*pRenderTargetTexture, 
		&renderTargetViewDesc, &*pRenderTargetView);

	// Setup the description of the shader resource view.
	shaderResourceViewDesc.Format = textureDesc.Format;
	shaderResourceViewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	shaderResourceViewDesc.Texture2D.MostDetailedMip = 0;
	shaderResourceViewDesc.Texture2D.MipLevels = 1;

	// Create the shader resource view.
	TheScreenManager::Instance()->GetDevice()->CreateShaderResourceView(*pRenderTargetTexture, 
		&shaderResourceViewDesc, &*pShaderResourceView);
	return true;
}

void DX11::Rotate(float angle, float x, float y, float z)
{
	// later replace this func with shader

/*	if (x != 0)
	{
		*TheScreenManager::Instance()->GetWorldMatrix() = XMMatrixRotationX(angle);
	}
	*TheScreenManager::Instance()->GetWorldMatrix() *= XMMatrixRotationY(angle);
	*TheScreenManager::Instance()->GetWorldMatrix() *= XMMatrixRotationZ(angle);*/	
	
	//float curTime = TheTimer::Instance()->GetTime();

	static float curTime = 0;

	if( TheScreenManager::Instance()->GetDriverType() == D3D_DRIVER_TYPE_REFERENCE )
    {
        curTime += ( float )D3DX_PI * 0.0225f;
    }
    else
    {
        static DWORD dwTimeStart = 0;
        DWORD dwTimeCur = GetTickCount();
        if( dwTimeStart == 0 )
            dwTimeStart = dwTimeCur;
        curTime = ( dwTimeCur - dwTimeStart ) / 1000.0f;
    }

	//*TheScreenManager::Instance()->GetWorldMatrix() = XMMatrixRotationX(curTime);
	//*TheObjMeshManager::Instance()->GetMeshWorld("crate2") = XMMatrixRotationY(curTime);
	//*TheObjMeshManager::Instance()->GetMeshWorld("crate2") *= XMMatrixTranslation(0, 0, 30);

	//XMMATRIX tempWorldMatrix;
	//ZeroMemory(&tempWorldMatrix, sizeof(tempWorldMatrix));

	//TheObjMeshManager::Instance()->GetMeshWorld("crate2", tempWorldMatrix);

	D3DXMATRIX tempMatrix = TheObjMeshManager::Instance()->GetMeshWorld("crate2");
	D3DXMatrixRotationY(&tempMatrix, curTime);
	//tempMatrix *= XMMatrixTranslation(0, 0, 30);
	TheObjMeshManager::Instance()->SetWorldMatrix("crate2", tempMatrix);

	

	//*TheScreenManager::Instance()->GetWorldMatrix() *= XMMatrixScaling(0.1f, 0.1f, 0.1f);
}

bool XMMatrixCompare(const D3DXMATRIX& lh, const D3DXMATRIX& rh)
{
	return	lh._11 == rh._11 && lh._12 == rh._12 &&	lh._13 == rh._13 &&	lh._14 == rh._14 &&
			lh._21 == rh._21 &&	lh._22 == rh._22 &&	lh._23 == rh._23 &&	lh._24 == rh._24 &&
			lh._31 == rh._31 &&	lh._32 == rh._32 &&	lh._33 == rh._33 &&	lh._34 == rh._34 &&
			lh._41 == rh._41 &&	lh._42 == rh._42 &&	lh._43 == rh._43 &&	lh._44 == rh._44;
}

void DX11::RotateMesh(const std::string& meshName, float angle, float x, float y, float z)
{
	static float curTime = 0;
	if( TheScreenManager::Instance()->GetDriverType() == D3D_DRIVER_TYPE_REFERENCE )
	{
		curTime += ( float )D3DX_PI * 0.0225f;
	}
	else
	{
		static DWORD dwTimeStart = 0;
		DWORD dwTimeCur = GetTickCount();
		if( dwTimeStart == 0 )
			dwTimeStart = dwTimeCur;
		curTime = ( dwTimeCur - dwTimeStart ) / 1000.0f;
	}

	// TO FIX: lag with timer
	//XMMATRIX indentity = XMMatrixIdentity();

	D3DXMATRIX meshMat = TheObjMeshManager::Instance()->GetMeshWorld(meshName);
	D3DXMATRIX tempMat;

	D3DXMatrixRotationX(&tempMat, curTime * x);
	meshMat *= tempMat;
	D3DXMatrixRotationY(&tempMat, curTime * y);
	meshMat *= tempMat;
	D3DXMatrixRotationZ(&tempMat, curTime * z);
	meshMat *= tempMat;

	if (meshName == "floor")
	{
		D3DXMatrixTranslation(&tempMat, 0.0f, -4.0f, 0.0f);
		meshMat *= tempMat;
	}

	TheObjMeshManager::Instance()->SetWorldMatrix(meshName, meshMat);
}

// 2d to 3d mouse position conversion based on http://www.rastertek.com/dx11tut47.html

D3DXVECTOR3 DX11::Get3DMousePos(const D3DXVECTOR2& mousePos)
{
	D3DXVECTOR3 mousePos3d = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
	float screenWidth = (float)TheScreenManager::Instance()->GetWindowWidth();
	float screenHeight = (float)TheScreenManager::Instance()->GetWindowHeight();
	float convertedMouseX = 0.0f;
	float convertedMouseY = 0.0f;
	D3DXMATRIX invertedViewMat;
	D3DXMATRIX projMat = TheScreenManager::Instance()->GetProjectionMatrix();
	D3DXVECTOR3 rayDirection;
	D3DXVECTOR3 direction;
	D3DXVECTOR3 camPos = TheCameraManager::Instance()->GetCameraPos();
	D3DXVECTOR3 rayOrigin;
	D3DXMATRIX worldMat = TheScreenManager::Instance()->GetWorldMatrix();
	D3DXMATRIX viewMat = TheScreenManager::Instance()->GetViewMatrix();
	
	D3D10_VIEWPORT vp;
	vp.Height = screenHeight;
	vp.Width = screenWidth;
	vp.TopLeftX = 0;
	vp.TopLeftY = 0;
	vp.MinDepth = 0.0;
	vp.MaxDepth = 1000.0f;


	D3DXVECTOR3 output = D3DXVECTOR3(0.0f, 0.0f, 0.0f);

	//D3DXVec3
	convertedMouseX = ((2.0f * mousePos.x / screenWidth) - 1.0f) / projMat._11;
	convertedMouseY = (((2.0f * mousePos.y / screenHeight) - 1.0f) * -1.0f) / projMat._22;
	D3DXVECTOR3 values = D3DXVECTOR3(convertedMouseX, convertedMouseY, 1.0f);

	D3DXVec3Unproject(&output, &values, &vp, &projMat, &viewMat, &worldMat);
	return output;



	D3DXMatrixInverse(&invertedViewMat, NULL, &viewMat);

	direction.x = (convertedMouseX * invertedViewMat._11) + (convertedMouseY * invertedViewMat._21) + invertedViewMat._31;
	direction.y = (convertedMouseX * invertedViewMat._12) + (convertedMouseY * invertedViewMat._22) + invertedViewMat._32;
	direction.z = (convertedMouseX * invertedViewMat._13) + (convertedMouseY * invertedViewMat._23) + invertedViewMat._33;

	D3DXVECTOR3 mousePos2d;
	D3DXVECTOR3 result;

}